special_hot_water function¶

This note aims to explain how heatpro.special_hot_water.special_hot_water is working

In [1]:
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.io as pio

pio.renderers.default='notebook'
pd.options.plotting.backend = "plotly"

Prerequisites : External Factors and Induced Factors¶

External Factors are exogenous data.

In [2]:
from heatpro.external_factors import ExternalFactors, EXTERNAL_TEMPERATURE_NAME

df = pd.read_csv('../external_factors.csv',parse_dates=True,index_col=0)
df.index = pd.date_range(start='2021',freq='h',periods=8760)
df2 = df.copy()
df2.index = pd.date_range(start='2022',freq='h',periods=8760)
external_factors = ExternalFactors(pd.concat((df,df2)))
external_factors.data.astype(float).plot()

Induced factor comes from External Factors processing.
Cold water temperature correspond to temparature in drinking water distribution network.
Closed heating season correspond to a period where months have at least one day in the heating season. Consumption on Closed Heating Season while be used to evaluate a normalized heat consomption for hot water.

In [3]:
from heatpro.external_factors import closed_heating_season, burch_cold_water, basic_temperature_departure, basic_temperature_return, kasuda_soil_temperature

induced_factors = pd.concat((
                        closed_heating_season(external_factors),
                        burch_cold_water(external_factors),
                        basic_temperature_departure(external_factors, T_max_HS=110,
                                                    T_max_NHS=90, T_min_HS=80, T_min_NHS=70,
                                                    T_ext_mid=15, T_ext_min=-15),
                        basic_temperature_return(external_factors, 70, 60),
                        kasuda_soil_temperature(external_factors,d=3, alpha=0.078),
                            ),axis=1)
induced_factors.astype(float).plot().show()

fig = go.Figure(layout_title="Difference between Heating Season and Closed Heating Season")

fig.add_trace(
    go.Scatter(
    x = external_factors.data.index,
    y = external_factors.data.heating_season,
    name = "Heating Season",
    )
)
fig.add_trace(
    go.Scatter(
    x = induced_factors.index,
    y = induced_factors.closed_heating_season,
    name = "Closed Heating Season",
    )
)
fig.show()

Beginning with a monthly energy consumption for heating including hot water consumption.

In [4]:
from heatpro.temporal_demand import MonthlyHeatDemand
from heatpro.check import ENERGY_FEATURE_NAME

np.random.seed(22)
total_heating_including_hotwater = MonthlyHeatDemand(
                                    'total_heating',
                                    pd.DataFrame(
                                        np.random.rand(24) * 10_000 + 60_000,       # Heating demand randomly initialized
                                        index=pd.date_range('2021',freq='MS',end='2023',inclusive='left'), #Monthly indexing
                                        columns=[ENERGY_FEATURE_NAME],
                                        )
                                )
total_heating_including_hotwater.plot()

Monthly hot water consumption (In terms of quantity of hot water and not energy) is given. Sum on each year of weights equals 1 as it is a profile (consequently normalized). $$ \forall~year, \int_{year}Q(t)dt = \sum_{month\in year}Q(month)= 1 $$

In [5]:
monthly_hot_water_profile = pd.DataFrame(
    [1.13,1.11,1.04,1.04,1.0,0.93,0.8,0.74,0.98,1.0,1.09,1.14]*2,
    columns=['weight'],
    index=total_heating_including_hotwater.data.index,
            ) / 12 # To normalize
monthly_hot_water_profile.plot()
In [6]:
monthly_hot_water_profile.resample('YE').sum() #Sum over each year
Out[6]:
weight
2021-12-31 1.0
2022-12-31 1.0

Monthly profile is disaggregate into hourly profile taking into account number of day in each month and number of hour in each day

$$ \forall~year, \int_{year}Q(t)dt = \sum_{hour \in month,month\in year}Q(hour,month)= 1 $$

In [7]:
from heatpro.demand_profile import day_length_proportionnal_weight

hourly_hot_water_month_profile = (day_length_proportionnal_weight(monthly_hot_water_profile.index)\
                    /24 \
                    * monthly_hot_water_profile) \
                    .reindex(induced_factors.index).ffill()
In [8]:
fig = go.Figure(layout_title="Change to monthly weight to hourly weight (constant on month)")

# Add trace for previous monthly weight
fig.add_trace(
    go.Scatter(
        x=monthly_hot_water_profile.index,
        y=monthly_hot_water_profile.weight,
        name="Previous monthly weight",
        yaxis="y1"  # Assign to y-axis 1
    )
)

# Add trace for new hourly weight
fig.add_trace(
    go.Scatter(
        x=hourly_hot_water_month_profile.index,
        y=hourly_hot_water_month_profile.weight,
        name="New hourly weight",
        yaxis="y2"  # Assign to y-axis 2
    )
)

# Define y-axis properties
fig.update_layout(
    yaxis=dict(
        title="Previous monthly weight",
        side="left"  # Align with left side
    ),
    yaxis2=dict(
        title="New hourly weight",
        overlaying="y",  # Overlay on top of first y-axis
        side="right"  # Align with right side
    ),
    legend=dict(
        orientation="h",  # Horizontal orientation
        yanchor="top",  # Anchor to the top
        y=-0.1,  # Position below the plot
        x=0.5,
        xanchor="center"  # Anchor to the center
    )
)

# Show the figure
fig.show()

Sum of hourly weights over a year equal 1.

In [9]:
hourly_hot_water_month_profile.resample('YE').sum()
Out[9]:
weight
2021-12-31 1.0
2022-12-31 1.0

Energy consumption on Closed Non Heating Season (supposedly equals to energy consumption for hot water production)

In [10]:
from heatpro.external_factors import CLOSED_HEATING_SEASON_NAME

non_heating_season_consumption = total_heating_including_hotwater.\
                data[~induced_factors[CLOSED_HEATING_SEASON_NAME].\
                reindex(total_heating_including_hotwater.data.index)]\
                [ENERGY_FEATURE_NAME].sum()
print(f'Non heating season consumption : {non_heating_season_consumption:.0f} kWh')
print(f'Overall consumption : {total_heating_including_hotwater.data.thermal_energy_kWh.sum():.0f} kWh')
Non heating season consumption : 522462 kWh
Overall consumption : 1559876 kWh

Get hourly energy consumption using gap temperature through time. For now consumption is constant on each day.

$$ \forall~day, E(day) = \frac{\int_{day}Q(t)\cdot(T^{(\text{prod})}-T_t^{(\text{Cold Water})})dt}{\int_{\text{Non Heating Season}}Q(t)\cdot(T^{(\text{prod})}-T_t^{(\text{Cold Water})})dt} \cdot \int_{\text{Non Heating Season}} E(t)dt $$

$$ \forall~hour \in~day, E(hour) = P(hour) \cdot E(day) $$ where, $$ \sum_{hour\in day}P(hour) = 1 $$

In [11]:
Temperature_hot_water = 60

daily_hot_water_energy_consumption = (non_heating_season_consumption *\
                                    (hourly_hot_water_month_profile['weight'] * (Temperature_hot_water - induced_factors['cold_water_temperature']))\
                                        .groupby(hourly_hot_water_month_profile.index.date).transform('sum')/\
                                    (hourly_hot_water_month_profile['weight'] * (Temperature_hot_water - induced_factors['cold_water_temperature']))[~induced_factors['closed_heating_season']].sum())\
                                        .rename(ENERGY_FEATURE_NAME)
                                    
daily_hot_water_energy_consumption.plot().update_layout(title='Daily consumption')
In [12]:
from heatpro.demand_profile import apply_hourly_pattern, basic_hot_water_hourly_profile, basic_hot_water_monthly_profile
hot_water_raw_profil = {
			0: 0.01,
			1: 0,
			2: 0,
			3: 0,
			4: 0,
			5: 4.797,
			6: 3.543,
			7: 0.86,
			8: 0.43,
			9: 0.86,
			10: 1.3,
			11: 0,
			12: 0.43,
			13: 0.43,
			14: 0.43,
			15: 0,
			16: 1.3,
			17: 0.43,
			18: 3.01,
			19: 4.797,
			20: 1.373,
			21: 0,
			22: 0,
			23: 0
		}
hot_water_raw_profil = {key:value/24 for key,value in hot_water_raw_profil.items()}
hourly_hot_water_raw_day_profil = apply_hourly_pattern(daily_hot_water_energy_consumption.index,hot_water_raw_profil)

hourly_hot_water_day_profil = basic_hot_water_hourly_profile(
                                                        hourly_hot_water_raw_day_profil,
                                                        0.1,
                                                        0.1,
                                                    )

final_hourly_hot_water_energy_consumption = pd.DataFrame((daily_hot_water_energy_consumption * hourly_hot_water_day_profil['weight']).rename(ENERGY_FEATURE_NAME))
In [13]:
fig = go.Figure(layout_title="Integrating daily profile")

# Add trace for previous monthly weight
fig.add_trace(
    go.Scatter(
        x=final_hourly_hot_water_energy_consumption.index,
        y=final_hourly_hot_water_energy_consumption[ENERGY_FEATURE_NAME],
        name="Hourly consumption",
        yaxis="y1"  # Assign to y-axis 1
    )
)

# Add trace for previous monthly weight
fig.add_trace(
    go.Scatter(
        x=daily_hot_water_energy_consumption.index,
        y=daily_hot_water_energy_consumption/24,
        name="Hourly consumption",
        yaxis="y1"  # Assign to y-axis 1
    )
)

# Add trace for new hourly weight
fig.add_trace(
    go.Scatter(
        x=induced_factors.index,
        y=induced_factors.cold_water_temperature,
        name="Cold water temperature",
        yaxis="y2"  # Assign to y-axis 2
    )
)

# Define y-axis properties
fig.update_layout(
    yaxis=dict(
        title="Hourly consumption",
        side="left"  # Align with left side
    ),
    yaxis2=dict(
        title="Cold water temperature",
        overlaying="y",  # Overlay on top of first y-axis
        side="right"  # Align with right side
    ),
    legend=dict(
        orientation="h",  # Horizontal orientation
        yanchor="top",  # Anchor to the top
        y=-0.1,  # Position below the plot
        x=0.5,
        xanchor="center"  # Anchor to the center
    )
)

# Show the figure
fig.show()
In [14]:
from heatpro.temporal_demand import HourlyHeatDemand

hot_water_energy_load = HourlyHeatDemand('hot_water',final_hourly_hot_water_energy_consumption)
hot_water_energy_load.plot()
In [15]:
from heatpro.special_hot_water import special_hot_water

direct_hot_water_energy_load = special_hot_water(
                        external_factors,
                        total_heating_including_hotwater,
                        monthly_hot_water_profile,
                        Temperature_hot_water,
                        hourly_hot_water_day_profil,
                        name="hot_water")

direct_hot_water_energy_load.plot()